<?php
namespace visitor;
/**
 * 访问者模式:
 * 在不改变原有类的前提下 , 定义新操作
 * 前提: 主题类型不容易变更 , 比如 固定的男人女人 , 固定的 白天黑夜 , 不会说多一个中午,傍晚这样的对象出来
 * */
abstract class Human{
    protected $name;

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    abstract public function doVisitor(Visitor $visitor);
}
class Man extends Human{
    protected $name = '男人';

    public function doVisitor(Visitor $visitor)
    {
        $visitor->manDeal($this);
    }
}

class Woman extends Human{
    protected $name = '女人';

    public function doVisitor(Visitor $visitor)
    {
        $visitor->womanDeal($this);
    }
}


/**
 * 为什么要独立出来 ?
 * 其实也可以直接写在 Human 里面 , 然后子类重写 不同操作
 *
 * 如果直接写在原对象中 , 每一个操作都需要增加一个方法 , 比如: 成功的方法 , 失败的方法  , 伤心的方法 , 难过的方法
 * 每次去写 , 对原对象都会有更改 , 违背开闭原则
 *
 * 如果独立出来 , 通过双分派技术 , 即将自己的实例注入到 访问者中 , 与访问者架起一座桥梁实现 对自身的访问
 *
 * */
interface Visitor{
    public function manDeal(Human $man);
    public function womanDeal(Human $man);
}
/**
 * 成功时的操作
 * */
class SuccessVisitor implements Visitor {

    public function manDeal(Human $man)
    {
        echo $man->getName().'成功时,背后肯定有一个默默无闻的女人!'.PHP_EOL;
    }

    public function womanDeal(Human $man)
    {
        echo $man->getName().'成功时,背后肯定有一个扛不住事的男人!'.PHP_EOL;
    }
}
/**
 * 失败时的操作
 * */
class FailVisitor implements Visitor {

    public function manDeal(Human $man)
    {
        echo $man->getName().'失败时,一个人抽烟,谁也不用劝!'.PHP_EOL;
    }

    public function womanDeal(Human $man)
    {
        echo $man->getName().'失败时,大哭大闹,谁也劝不住!'.PHP_EOL;
    }
}

/**
 *  等等........ 可以很方便的扩展新操作
 * */

$man = new Man();
$woman = new Woman();

$visitor = new FailVisitor();
$success = new SuccessVisitor();

$man->doVisitor($visitor);
$woman->doVisitor($visitor);

$man->doVisitor($success);
$woman->doVisitor($success);